#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>

#include <linux/sockios.h>
#include <linux/types.h>

#define ZDAPIOCTL   (SIOCDEVPRIVATE)
#define ZDAPIOCTLGET    (SIOCDEVPRIVATE+0xB)

struct zdap_ioctl {
    __u16 cmd;                /* Command to run */
    __u32 addr;                /* Length of the data buffer */
    __u32 value;               /* Pointer to the data buffer */
    unsigned char  data[2048];
};


#define ZD_IOCTL_REG_READ           0x01
#define ZD_IOCTL_REG_WRITE          0x02
#define ZD_IOCTL_MEM_DUMP           0x03
#define ZD_IOCTL_RATE                   0x04
#define ZD_IOCTL_SNIFFER                0x05
#define ZD_IOCTL_CAM_DUMP               0x06
#define ZD_IOCTL_DUMP_PHY               0x07
#define ZD_IOCTL_CARD_SETTING           0x08
#define ZD_IOCTL_HASH_DUMP          0x09
#define ZD_IOCTL_RFD_DUMP           0x0A
#define ZD_IOCTL_MEM_READ           0x0B
#define ZD_IOCTL_MEM_WRITE          0x0C

//for STA
#define ZD_IOCTL_TX_RATE            0x0D
#define ZD_IOCTL_EEPROM             0x0E

#define ZD_IOCTL_BCN                0x10
#define ZD_IOCTL_REG_READ16         0x11
#define ZD_IOCTL_REG_WRITE16            0x12

//for CAM Test
#define ZD_IOCTL_CAM_READ           0x13
#define ZD_IOCTL_CAM_WRITE          0x14
#define ZD_IOCTL_CAM_RESET          0x15
#define ZD_IOCTL_READ_PHY           0x16
#define ZD_IOCTL_WRITE_PHY          0x17
#define ZD_IOCTL_CONT_TX            0x18
#define ZD_IOCTL_SET_MIC_CNT_ENABLE 0x19
#define ZD_IOCTL_GET_MIC_CNT_ENABLE 0x1A
#define ZD_IOCTL_DEBUG_FLAG         0x21
#define ZD_IOCTL_UW_PWR             0x29
#define ZD_IOCTL_SYSTEM_INFO             0x30
#define ZD_IOCTL_ANTDIVERSITY       0x31
#define ZD_IOCTL_ACS                0x32
#define ZD_IOCTL_HIDE_SSID          0x33
#define ZD_IOCTL_STA_LIST           0xF0
#define ZD_IOCTL_APC                0xF1
#define ZD_IOCTL_WPS_PBC            0xF2
#define ZD_IOCTL_TR_DBG             0xF3



int set_ioctl(int sock, struct ifreq *req)
{
    int i;
    int ret;
    ret = ioctl(sock, ZDAPIOCTL, req); 
    if(ret)
        printf("ret=%d\n",ret);
    
    return 0;

}


void showUsage()
{
    printf("Usage:\n");
    printf(" apdbg <ifname> [<operation>] [<address>(0)] [<value>(0)]\n\n");
    printf("\n");
    printf("valid operation: mem, phy\n");
    printf("camdump, card, rfds, rmem, wmem, rate\n");
    printf("hash, eep, rdcam, wrcam, rstcam\n");
    printf("uwpwr\n");
    printf("##### Hardware Memory Operation ######\n");
    printf(" dumpphy       Dump all PHY Registers\n");
    printf(" rdphy         Read Phy Register\n");
    printf(" wrphy         Write Phy Register\n");
    printf(" read          Read MAC Register\n");
    printf(" write         Write MAC Register\n");
    printf("##### Feature Control Operation #####\n");
    printf(" g_mic_cnt     Get If WPA counter measure enabled.\n");
    printf(" s_mic_cnt     Set WPA counter measure feature\n");
    printf(" txrate        Set Fixed Transmission Rate\n");
    printf("##### Production Operation #####\n");
    printf("tx_cont        Activate Continous TX\n");
    printf("##### For CAM function\n");
    printf("camdump        <Addr> <Length>\n");
    printf("rdcam          <Addr>\n");
    printf("wrcam          <Addr> <Data>\n");
    printf("##### Managment ######\n");
    printf("list_sta        List associated Stations Information\n");
    exit(1);
}
void trdbg(int argc, char **argv)
{
    struct ifreq req;
    struct zdap_ioctl zdreq;
    int i, sock;

    memset(&req, 0, sizeof(req));
    strcpy(req.ifr_name, argv[1]);
    sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sock < 0) {
        printf("%s: socket(): %s\n", argv[0], strerror(errno));
        exit(1);
    }
    sscanf(argv[3], "%x", &i);
    memset(&zdreq, 0, sizeof(zdreq));
    zdreq.cmd = ZD_IOCTL_TR_DBG;
    zdreq.addr = i;
    zdreq.value = i;
    printf("Issue TRDBG with addr=%d\n",i);
    req.ifr_data = (char *)&zdreq;
    set_ioctl(sock, &req);
    close(sock);
}
void sysinfo(int argc, char **argv)
{
    struct ifreq req;
    struct zdap_ioctl zdreq;
    int i, sock;

    memset(&req, 0, sizeof(req));
    strcpy(req.ifr_name, argv[1]);
    sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sock < 0) {
        printf("%s: socket(): %s\n", argv[0], strerror(errno));
        exit(1);
    }
    memset(&zdreq, 0, sizeof(zdreq));
    zdreq.cmd = ZD_IOCTL_SYSTEM_INFO;
    zdreq.addr = i;
    zdreq.value = i;
    req.ifr_data = (char *)&zdreq;
    set_ioctl(sock, &req);
    close(sock);
}
void camdump(int argc, char **argv)
{
    struct ifreq req;
    struct zdap_ioctl zdreq;
    int i, sock;

    memset(&req, 0, sizeof(req));
    strcpy(req.ifr_name, argv[1]);
    sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sock < 0) {
        printf("%s: socket(): %s\n", argv[0], strerror(errno));
        exit(1);
    }
    for(i=0;i<=43;i++)
    {
        memset(&zdreq, 0, sizeof(zdreq));
        zdreq.cmd = ZD_IOCTL_CAM_DUMP;
        zdreq.addr = i;
        zdreq.value = i;
        req.ifr_data = (char *)&zdreq;
        set_ioctl(sock, &req);
    }
    close(sock);
}
void antdiversity(int argc, char **argv)
{
    struct ifreq req;
    struct zdap_ioctl zdreq;
    int i, sock;

    memset(&req, 0, sizeof(req));
    strcpy(req.ifr_name, argv[1]);
    sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sock < 0) {
        printf("%s: socket(): %s\n", argv[0], strerror(errno));
        exit(1);
    }
    memset(&zdreq, 0, sizeof(zdreq));
    zdreq.cmd = ZD_IOCTL_ANTDIVERSITY;
    sscanf(argv[3], "%x", &zdreq.addr);
    sscanf(argv[4], "%x", &zdreq.value);
    req.ifr_data = (char *)&zdreq;
    set_ioctl(sock, &req);
    close(sock);
}

int main(int argc, char **argv)
{
    int sock;
    int addr, value;
    struct ifreq req;
    char *action = NULL;
    struct zdap_ioctl zdreq;
    int mode = 0;


    if (argc < 5) 
        showUsage();


    strcpy(req.ifr_name, argv[1]);
    zdreq.addr = 0;
    zdreq.value = 0;

    /* a silly raw socket just for ioctl()ling it */
    sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sock < 0) {
        printf("%s: socket(): %s\n", argv[0], strerror(errno));
        exit(1);
    }

    sscanf(argv[3], "%x", &addr);
    sscanf(argv[4], "%x", &value);
    zdreq.addr = addr;
    zdreq.value = value;

    if (!strcmp(argv[2], "read")) {
        zdreq.cmd = ZD_IOCTL_REG_READ;
    goto just_set;
    }
    else if (!strcmp(argv[2], "mem")) {
        zdreq.cmd = ZD_IOCTL_MEM_DUMP;
        goto just_set;
    }       
    else if (!strcmp(argv[2], "write")) {
        zdreq.cmd = ZD_IOCTL_REG_WRITE;
        goto just_set;
    }   
    else if (!strcmp(argv[2], "pbc")) {
        zdreq.cmd = ZD_IOCTL_WPS_PBC; 
        goto just_set;
    }
    else if (!strcmp(argv[2], "sniffer")) {
        zdreq.cmd = ZD_IOCTL_SNIFFER;
        goto just_set;
    }
    else if (!strcmp(argv[2], "hidessid")) {
        zdreq.cmd = ZD_IOCTL_HIDE_SSID;
        goto just_set;
    }
    else if (!strcmp(argv[2], "acs"))
    {
        zdreq.cmd = ZD_IOCTL_ACS;
        goto just_set;
    }

    else if (!strcmp(argv[2], "camdump")) {
        zdreq.cmd = ZD_IOCTL_CAM_DUMP;
        goto just_set;

    }
    else if (!strcmp(argv[2], "ant")) {
        antdiversity(argc, argv);
        return;
    }

    else if (!strcmp(argv[2], "trdbg")) {
        trdbg(argc, argv);
        return;
    }
    else if (!strcmp(argv[2], "sysinfo")) {
        zdreq.cmd = ZD_IOCTL_SYSTEM_INFO;
        goto just_set;

    }
    else if (!strcmp(argv[2], "dumpphy")) {
        zdreq.cmd = ZD_IOCTL_DUMP_PHY;
        goto just_set;
    }
    else if (!strcmp(argv[2], "rdphy")){
    zdreq.cmd = ZD_IOCTL_READ_PHY;
        goto just_set;
    }
    else if (!strcmp(argv[2], "wrphy")){
    zdreq.cmd = ZD_IOCTL_WRITE_PHY;
        goto just_set;
    }

    else if (!strcmp(argv[2], "card")) {
        zdreq.cmd = ZD_IOCTL_CARD_SETTING;
        goto just_set;
    }
    else if (!strcmp(argv[2], "hash")) {
        zdreq.cmd = ZD_IOCTL_HASH_DUMP;
        goto just_set;
    }
    else if (!strcmp(argv[2], "rfd")) {
        zdreq.cmd = ZD_IOCTL_RFD_DUMP;
        goto just_set;
    }    
    else if (!strcmp(argv[2], "rmem")) {
        zdreq.cmd = ZD_IOCTL_MEM_READ;
        goto just_set;
    }
    else if (!strcmp(argv[2], "wmem")) {
        zdreq.cmd = ZD_IOCTL_MEM_WRITE;
        goto just_set;
    }
    else if (!strcmp(argv[2], "txrate")) {
        zdreq.cmd = ZD_IOCTL_TX_RATE;
        goto just_set;
    }
    else if (!strcmp(argv[2], "eep")) {
        zdreq.cmd = ZD_IOCTL_EEPROM;
        goto just_set;
    }
    else if (!strcmp(argv[2], "rate")) {
        zdreq.cmd = ZD_IOCTL_RATE;
    goto just_set;
    }
    else if(!strcmp(argv[2], "cont_tx")) {
        zdreq.cmd = ZD_IOCTL_CONT_TX;
    goto just_set;
    }
    else if (!strcmp(argv[2], "bcn")) {
    zdreq.cmd = ZD_IOCTL_BCN;
    goto just_set;
    }
    else if (!strcmp(argv[2], "read16")) {
    zdreq.cmd = ZD_IOCTL_REG_READ16;
    goto just_set;
    }
    else if (!strcmp(argv[2], "write16")) {
    zdreq.cmd = ZD_IOCTL_REG_WRITE16;
    goto just_set;
    }
    else if (!strcmp(argv[2], "rdcam")) {
        mode = 1;
        zdreq.cmd = ZD_IOCTL_CAM_READ;
        goto just_set;
    }
    else if (!strcmp(argv[2], "wrcam")) {
        mode = 1;
        zdreq.cmd = ZD_IOCTL_CAM_WRITE;
        goto just_set;
    }
    else if (!strcmp(argv[2], "rstcam")) {
        zdreq.cmd = ZD_IOCTL_CAM_RESET;
        goto just_set;
    }
    else if (!strcmp(argv[2], "g_mic_cnt")) {
        zdreq.cmd = ZD_IOCTL_GET_MIC_CNT_ENABLE;
        goto just_set;
    }
    else if (!strcmp(argv[2], "s_mic_cnt")) {
        zdreq.cmd = ZD_IOCTL_SET_MIC_CNT_ENABLE;
        goto just_set;
    }
    else if (!strcmp(argv[2], "uwpwr"))
    {
        zdreq.cmd = ZD_IOCTL_UW_PWR;
        goto just_set;
    }
    else if( !strcmp(argv[2], "list_sta")) {
        zdreq.cmd = ZD_IOCTL_STA_LIST;
        goto just_set;
    }
    else if (!strcmp(argv[2], "debugflag"))
    {
        zdreq.cmd = ZD_IOCTL_DEBUG_FLAG;
        goto just_set;
    }
    else if (!strcmp(argv[2], "apc"))
    {
        zdreq.cmd = ZD_IOCTL_APC;
        goto just_set;
    }
    else {
    printf("error action\n");
        exit(1);
    }

 just_set:

    if(mode == 0)
        sscanf(argv[3], "%x", &addr);
    else
        sscanf(argv[3], "%d", &addr);

    sscanf(argv[4], "%x", &value);

    zdreq.addr = addr;
    zdreq.value = value;
    req.ifr_data = (char *)&zdreq;
    set_ioctl(sock, &req);

fail:
    exit(0);
}



